Skip to content

feat: implement sign-up page with form validation and shared layout structure#1

Merged
thyuhtooaung-dev merged 4 commits intomainfrom
feat/auth
Apr 17, 2026
Merged

feat: implement sign-up page with form validation and shared layout structure#1
thyuhtooaung-dev merged 4 commits intomainfrom
feat/auth

Conversation

@thyuhtooaung-dev
Copy link
Copy Markdown
Owner

@thyuhtooaung-dev thyuhtooaung-dev commented Apr 17, 2026

Summary by CodeRabbit

  • New Features

    • Added a sign-up form with validation for name, email, and password fields
    • Introduced a back-to-home navigation link on the sign-up page
    • Implemented a shared layout with integrated navigation
  • Improvements

    • Updated visual design with a new font
    • Enhanced code quality through automated formatting, linting, and build checks
  • Dependencies

    • Added form handling and schema validation packages

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 17, 2026

Warning

Rate limit exceeded

@thyuhtooaung-dev has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 55 minutes and 17 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 55 minutes and 17 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cc0ffac9-c303-4b54-8728-0a5500ecbb7d

📥 Commits

Reviewing files that changed from the base of the PR and between 9b9c56c and 99f1d83.

📒 Files selected for processing (1)
  • pnpm-workspace.yaml
📝 Walkthrough

Walkthrough

This update introduces a CI/CD workflow for quality checks, restructures the app layout with a shared wrapper, implements form validation and sign-up functionality using Zod and react-hook-form, adds UI form components, standardizes code punctuation, and updates dependencies to support form handling.

Changes

Cohort / File(s) Summary
CI/CD & Configuration
.github/workflows/ci.yml, AGENTS.md
Added GitHub Actions workflow for quality checks (format, lint, build) on push/PR to main branch with concurrency controls; adjusted blank lines in agent rules marker.
Layout Structure
app/(shared-layout)/layout.tsx, app/(shared-layout)/page.tsx, app/auth/sign-up/layout.tsx, app/auth/sign-up/page.tsx, app/auth/login/page.tsx
Introduced shared layout wrapper with Navbar and auth layout with back navigation; created sign-up form with Zod-validated inputs (name, email, password); updated page files with trailing punctuation normalization.
Root Layout & Styling
app/layout.tsx, app/globals.css
Replaced Geist font with Outfit, removed Navbar from root layout (now in shared layout), added font-sans utility class; normalized end-of-file newlines.
Form Validation Schema
app/schemas/auth.ts
Added Zod schema for sign-up validation with constraints for name (3-30 chars), email format, and password (minimum 6 chars).
UI Form Components
components/ui/field.tsx, components/ui/input.tsx, components/ui/label.tsx, components/ui/separator.tsx
Created composable field UI components for forms including Field, FieldLabel, FieldError (with deduplication logic), FieldDescription, FieldSeparator, Input wrapper, and Label wrapper wrapping Radix UI primitives.
UI Component Updates
components/ui/button.tsx, components/ui/dropdown-menu.tsx
Standardized semicolon punctuation across exports and statements; refactored dropdown components to use multi-argument cn() calls and improved JSX formatting.
Web Components & Utils
components/web/navbar.tsx, lib/utils.ts
Normalized spacing/formatting in Navbar JSX and buttonVariants; standardized semicolon punctuation in utils module.
Dependencies & Scripts
package.json
Added npm scripts for format (Prettier write) and format:check (Prettier verify); added form-handling packages (react-hook-form, @hookform/resolvers) and schema validation (zod).

Sequence Diagram

sequenceDiagram
    participant User
    participant SignupForm as Signup Form UI
    participant ReactHookForm as react-hook-form
    participant ZodValidator as Zod Validator
    participant Handler as onSubmit Handler

    User->>SignupForm: Enter name, email, password
    User->>SignupForm: Click "Sign up" button
    SignupForm->>ReactHookForm: form.handleSubmit(onSubmit)
    ReactHookForm->>ZodValidator: Validate against signUpSchema
    alt Validation Passes
        ZodValidator-->>ReactHookForm: ✓ Valid data
        ReactHookForm->>Handler: onSubmit(validated data)
        Handler->>Handler: Log "yoo"
    else Validation Fails
        ZodValidator-->>ReactHookForm: ✗ Errors (name, email, password)
        ReactHookForm->>SignupForm: Update field errors
        SignupForm->>User: Display error messages
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 A form takes shape with fields so fine,
With Zod to validate each line,
Navbar hops to layouts new,
Components bloom in UI blue,
The rabbit's burrow code runs true! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: implementing a sign-up page with form validation (react-hook-form, zod schema) and shared layout structure (SharedLayout component, auth layout wrapper).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/auth

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (3)
.github/workflows/ci.yml (1)

1-42: CI workflow looks good.

Concurrency cancel, frozen lockfile install, and the format:checklintbuild sequence are appropriate. Optional: consider adding a packageManager field in package.json so the pnpm version stays in sync with the workflow, but not required.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci.yml around lines 1 - 42, Add a packageManager entry to
package.json to lock the repository to the pnpm version used by the CI: open
package.json and add "packageManager": "pnpm@9" at the root so it aligns with
the workflow (name: Nextblog CI) which uses the quality-check job step Install
pnpm (uses: pnpm/action-setup@v3, with version: 9); ensure the packageManager
string matches the version specified in that step so local tooling and CI remain
in sync.
app/schemas/auth.ts (1)

8-8: Prefer the Zod 4 top-level z.email() schema.

Zod 4 introduces standalone string-format schemas (e.g., z.email(), z.uuid()) as the recommended API; z.string().email() is the legacy v3 style. Migrating keeps this aligned with the pinned zod@4.3.6.

♻️ Proposed refactor
-  email: z.string().email("Invalid email address"),
+  email: z.email("Invalid email address"),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/schemas/auth.ts` at line 8, The schema uses the older z.string().email()
API for the email field; replace that with the Zod v4 top-level z.email() schema
(update the email property in the relevant schema object that currently contains
"email: z.string().email(...)") so it uses "email: z.email(...)" and preserve
the existing custom error message text.
components/ui/field.tsx (1)

197-197: Minor nits in FieldError.

  • Line 197: use strict equality (===) instead of ==.
  • Line 205: key={index} is fine, but since uniqueErrors is already deduplicated by error.message, using the message as the key is more stable across renders.
🛠 Proposed fix
-    if (uniqueErrors?.length == 1) {
+    if (uniqueErrors?.length === 1) {
       return uniqueErrors[0]?.message;
     }
@@
-        {uniqueErrors.map(
-          (error, index) =>
-            error?.message && <li key={index}>{error.message}</li>,
-        )}
+        {uniqueErrors.map(
+          (error) =>
+            error?.message && <li key={error.message}>{error.message}</li>,
+        )}

Also applies to: 203-206

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/ui/field.tsx` at line 197, In the FieldError rendering logic,
replace the non-strict equality check on uniqueErrors length with a strict
comparison (use uniqueErrors?.length === 1) and change the list item key from
the index to the stable deduplication key (use error.message as the key) so the
map in the FieldError component uses key={error.message} instead of key={index};
update occurrences where uniqueErrors is iterated (the map that renders error
items) and the conditional that checks its length accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/auth/sign-up/layout.tsx`:
- Around line 11-19: The outer layout div uses an absolutely positioned "Back to
Home" Link but the container div (the one rendering children) lacks a
positioning context; update the outer <div className="min-h-screen flex
items-center justify-center"> to include relative positioning (e.g., add
"relative") so the absolute positioning of the Link anchors to this layout
component; change the className where children are rendered (the container
wrapping Link, ArrowLeft, buttonVariants and children) to include "relative".
- Line 5: The default exported React component named authLayout should be
renamed to PascalCase: AuthLayout; update the function declaration/export from
authLayout to AuthLayout and update any imports/usages that reference authLayout
to import or reference AuthLayout instead, and ensure the component name inside
JSX/Next layout reference matches the new AuthLayout identifier to satisfy
React/ESLint conventions.

In `@app/auth/sign-up/page.tsx`:
- Around line 50-60: The FieldLabel and Input are currently siblings so the
label isn't associated with the control; update each field (the Full Name,
Email, and Password blocks using Field, FieldLabel, Input, FieldError) to either
nest Input inside FieldLabel or give the Input a unique id and set FieldLabel's
htmlFor to that id so clicking the label focuses the input and assistive tech
announces it; also correct the placeholder typos from "Jhon" to "John".
- Around line 33-35: Replace the placeholder onSubmit with a real handler that
accepts the validated data parameter typed as z.infer<typeof signUpSchema>
(e.g., const onSubmit = async (data: z.infer<typeof signUpSchema>) => { ... })
and perform the actual submit logic there; add the noValidate attribute to the
<form> element so browser native validation does not short-circuit the
Zod/resolver flow; and guard the submit button using form.formState.isSubmitting
(disable the button when isSubmitting is true) to prevent duplicate submits once
async work is wired in.
- Line 25: Remove the explicit any cast from the zodResolver usage: replace the
call using "zodResolver(signUpSchema as any)" with "zodResolver(signUpSchema)"
so TypeScript can infer types; update the resolver argument in the useForm
invocation (the resolver passed to useForm in page.tsx) to use
zodResolver(signUpSchema) directly and confirm the form type is inferred from
z.infer<typeof signUpSchema> (no other code changes required).

In `@components/ui/field.tsx`:
- Around line 118-129: FieldTitle currently uses the same data-slot as
FieldLabel which conflicts with fieldVariants selectors; change FieldTitle's
data-slot from "field-label" to a unique value such as "field-title" in the
FieldTitle component so it no longer collides with FieldLabel, and then update
any related CSS/variant selectors that should target the title slot (e.g.,
selectors referencing data-[slot=field-label] that are meant for the title) to
use data-[slot=field-title] or leave them untouched if they should only target
labels.

In `@components/ui/separator.tsx`:
- Around line 19-22: The Tailwind data-attribute variants are wrong: update the
className in the Separator component (the one building classes passed to
SeparatorPrimitive.Root / cn) to target Radix's data-orientation attribute
instead of non-existent data-horizontal/data-vertical; replace
data-horizontal:... and data-vertical:... with the attribute selector variants
(e.g. data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full
and data-[orientation=vertical]:w-px data-[orientation=vertical]:self-stretch)
so the horizontal/vertical sizing rules actually apply.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 1-42: Add a packageManager entry to package.json to lock the
repository to the pnpm version used by the CI: open package.json and add
"packageManager": "pnpm@9" at the root so it aligns with the workflow (name:
Nextblog CI) which uses the quality-check job step Install pnpm (uses:
pnpm/action-setup@v3, with version: 9); ensure the packageManager string matches
the version specified in that step so local tooling and CI remain in sync.

In `@app/schemas/auth.ts`:
- Line 8: The schema uses the older z.string().email() API for the email field;
replace that with the Zod v4 top-level z.email() schema (update the email
property in the relevant schema object that currently contains "email:
z.string().email(...)") so it uses "email: z.email(...)" and preserve the
existing custom error message text.

In `@components/ui/field.tsx`:
- Line 197: In the FieldError rendering logic, replace the non-strict equality
check on uniqueErrors length with a strict comparison (use uniqueErrors?.length
=== 1) and change the list item key from the index to the stable deduplication
key (use error.message as the key) so the map in the FieldError component uses
key={error.message} instead of key={index}; update occurrences where
uniqueErrors is iterated (the map that renders error items) and the conditional
that checks its length accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4eba9a19-e96a-42b3-a9dc-df8c5368bb56

📥 Commits

Reviewing files that changed from the base of the PR and between fac3d8f and 9b9c56c.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (19)
  • .github/workflows/ci.yml
  • AGENTS.md
  • app/(shared-layout)/layout.tsx
  • app/(shared-layout)/page.tsx
  • app/auth/login/page.tsx
  • app/auth/sign-up/layout.tsx
  • app/auth/sign-up/page.tsx
  • app/globals.css
  • app/layout.tsx
  • app/schemas/auth.ts
  • components/ui/button.tsx
  • components/ui/dropdown-menu.tsx
  • components/ui/field.tsx
  • components/ui/input.tsx
  • components/ui/label.tsx
  • components/ui/separator.tsx
  • components/web/navbar.tsx
  • lib/utils.ts
  • package.json

import { ArrowLeft } from "lucide-react";
import Link from "next/link";

export default function authLayout({
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Rename default export to PascalCase.

React component convention (and ESLint react/* rules commonly used with Next.js) requires component names to start with an uppercase letter. authLayout should be AuthLayout.

-export default function authLayout({
+export default function AuthLayout({
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export default function authLayout({
export default function AuthLayout({
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/auth/sign-up/layout.tsx` at line 5, The default exported React component
named authLayout should be renamed to PascalCase: AuthLayout; update the
function declaration/export from authLayout to AuthLayout and update any
imports/usages that reference authLayout to import or reference AuthLayout
instead, and ensure the component name inside JSX/Next layout reference matches
the new AuthLayout identifier to satisfy React/ESLint conventions.

Comment on lines +11 to +19
<div className="min-h-screen flex items-center justify-center">
<div className="absolute top-5 left-5">
<Link href="/" className={buttonVariants({ variant: "secondary" })}>
<ArrowLeft className="size-4" />
Back to Home
</Link>
</div>
<div className="w-full max-w-md mx-auto">{children}</div>
</div>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

absolute without a positioned ancestor.

The wrapper is not relative, so the "Back to Home" link positions against the nearest positioned ancestor (likely the viewport/<body>). If the intent is to anchor the back link to this layout's container (e.g., for future nesting or modal contexts), add relative to the outer div.

-    <div className="min-h-screen flex items-center justify-center">
+    <div className="relative min-h-screen flex items-center justify-center">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/auth/sign-up/layout.tsx` around lines 11 - 19, The outer layout div uses
an absolutely positioned "Back to Home" Link but the container div (the one
rendering children) lacks a positioning context; update the outer <div
className="min-h-screen flex items-center justify-center"> to include relative
positioning (e.g., add "relative") so the absolute positioning of the Link
anchors to this layout component; change the className where children are
rendered (the container wrapping Link, ArrowLeft, buttonVariants and children)
to include "relative".

Comment thread app/auth/sign-up/page.tsx
return <h1>Sign Up Page</h1>
} No newline at end of file
const form = useForm<z.infer<typeof signUpSchema>>({
resolver: zodResolver(signUpSchema as any),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check installed versions of hookform resolvers, react-hook-form, and zod
fd -t f 'package.json' -d 2 --exec cat {} \; | jq '.dependencies + .devDependencies | with_entries(select(.key | test("react-hook-form|@hookform/resolvers|zod")))'

Repository: thyuhtooaung-dev/NextBlog

Length of output: 157


🌐 Web query:

Does @hookform/resolvers latest zodResolver require a cast for Zod 4 schemas?

💡 Result:

No, the latest @hookform/resolvers zodResolver does not require a cast (like schema.cast from Zod 3) for Zod 4 schemas. It fully supports Zod 4 natively since version 5.1.0 (June 2025), with ongoing fixes in later releases up to 5.2.2 (Sep 2025). Usage is direct: import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; // or 'zod/v4' const schema = z.object({ ... }); useForm({ resolver: zodResolver(schema) }); Official docs and examples explicitly show import { z } from 'zod'; // or 'zod/v4' with no casting needed. Types are automatically inferred from the schema (input/output distinction handled internally). Zod 4 changes (no preprocess/castDraft) are adapted in the resolver source, using schema.parse/parseAsync on form values directly. Zod 3 sometimes needed schema.cast for loose parsing, but Zod 4/ZodResolver combo uses strict parse by default, with raw mode option for untransformed values.

Citations:


Remove the as any cast on zodResolver.

ESLint flags this (@typescript-eslint/no-explicit-any). The installed versions (@hookform/resolvers 5.2.2 and Zod 4.3.6) support zodResolver(signUpSchema) directly without the cast. The type inference from z.infer<typeof signUpSchema> works correctly on useForm when the cast is removed.

Proposed fix
-    resolver: zodResolver(signUpSchema as any),
+    resolver: zodResolver(signUpSchema),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
resolver: zodResolver(signUpSchema as any),
resolver: zodResolver(signUpSchema),
🧰 Tools
🪛 ESLint

[error] 25-25: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/auth/sign-up/page.tsx` at line 25, Remove the explicit any cast from the
zodResolver usage: replace the call using "zodResolver(signUpSchema as any)"
with "zodResolver(signUpSchema)" so TypeScript can infer types; update the
resolver argument in the useForm invocation (the resolver passed to useForm in
page.tsx) to use zodResolver(signUpSchema) directly and confirm the form type is
inferred from z.infer<typeof signUpSchema> (no other code changes required).

Comment thread app/auth/sign-up/page.tsx
Comment on lines +33 to +35
const onSubmit = () => {
console.log("yoo");
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Placeholder onSubmit and missing submit-state handling.

A few things to address before this is merge-ready:

  1. onSubmit just logs "yoo" — needs a real implementation (and the handler should accept the validated data parameter typed as z.infer<typeof signUpSchema>).
  2. The <form> has no noValidate, so the browser’s native validation (triggered by type="email") will run alongside Zod and can short-circuit your resolver’s error messages.
  3. The submit button isn’t guarded by form.formState.isSubmitting, allowing duplicate submissions once async work is wired in.
🛠 Suggested shape
-  const onSubmit = () => {
-    console.log("yoo");
-  };
+  const onSubmit = async (data: z.infer<typeof signUpSchema>) => {
+    // TODO: submit to auth endpoint
+    console.log(data);
+  };
@@
-        <form onSubmit={form.handleSubmit(onSubmit)}>
+        <form onSubmit={form.handleSubmit(onSubmit)} noValidate>
@@
-            <Button type="submit">Sign up</Button>
+            <Button type="submit" disabled={form.formState.isSubmitting}>
+              Sign up
+            </Button>

Want me to open a follow-up issue to track the real submit implementation (API call, error surface, redirect)?

Also applies to: 44-44, 100-100

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/auth/sign-up/page.tsx` around lines 33 - 35, Replace the placeholder
onSubmit with a real handler that accepts the validated data parameter typed as
z.infer<typeof signUpSchema> (e.g., const onSubmit = async (data: z.infer<typeof
signUpSchema>) => { ... }) and perform the actual submit logic there; add the
noValidate attribute to the <form> element so browser native validation does not
short-circuit the Zod/resolver flow; and guard the submit button using
form.formState.isSubmitting (disable the button when isSubmitting is true) to
prevent duplicate submits once async work is wired in.

Comment thread app/auth/sign-up/page.tsx
Comment on lines +50 to +60
<Field>
<FieldLabel>Full Name</FieldLabel>
<Input
aria-invalid={fieldState.invalid}
placeholder="Jhon Doe"
{...field}
/>
{fieldState.error && (
<FieldError>{fieldState.error.message}</FieldError>
)}
</Field>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Associate FieldLabel with its Input (accessibility).

FieldLabel wraps Radix Label, which associates with a control either by nesting or via htmlFor/id. Here the label is a sibling of Input with neither set, so clicking the label won’t focus the input and assistive tech won’t announce the label with the field. The same applies to the email and password fields.

🛠 Proposed fix (apply to all three fields)
               render={({ field, fieldState }) => (
                 <Field>
-                  <FieldLabel>Full Name</FieldLabel>
+                  <FieldLabel htmlFor="name">Full Name</FieldLabel>
                   <Input
+                    id="name"
                     aria-invalid={fieldState.invalid}
-                    placeholder="Jhon Doe"
+                    placeholder="John Doe"
                     {...field}
                   />

Note: the "Jhon" placeholders on lines 54 and 71 are also typos for "John".

Also applies to: 67-78, 85-96

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/auth/sign-up/page.tsx` around lines 50 - 60, The FieldLabel and Input are
currently siblings so the label isn't associated with the control; update each
field (the Full Name, Email, and Password blocks using Field, FieldLabel, Input,
FieldError) to either nest Input inside FieldLabel or give the Input a unique id
and set FieldLabel's htmlFor to that id so clicking the label focuses the input
and assistive tech announces it; also correct the placeholder typos from "Jhon"
to "John".

Comment thread components/ui/field.tsx
Comment on lines +118 to +129
function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="field-label"
className={cn(
"flex w-fit items-center gap-2 text-sm font-medium group-data-[disabled=true]/field:opacity-50",
className,
)}
{...props}
/>
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

FieldTitle has the wrong data-slot value.

FieldTitle sets data-slot="field-label", the same slot used by FieldLabel (line 107). This collides with selectors in fieldVariants (e.g. *:data-[slot=field-label]:flex-auto on line 61) and with FieldLabel’s own has-[>[data-slot=field]]:… styles (line 110), causing unintended layout/styling whenever FieldTitle is used inside a Field. It should be its own slot.

🛠 Proposed fix
 function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
   return (
     <div
-      data-slot="field-label"
+      data-slot="field-title"
       className={cn(
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="field-label"
className={cn(
"flex w-fit items-center gap-2 text-sm font-medium group-data-[disabled=true]/field:opacity-50",
className,
)}
{...props}
/>
);
}
function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="field-title"
className={cn(
"flex w-fit items-center gap-2 text-sm font-medium group-data-[disabled=true]/field:opacity-50",
className,
)}
{...props}
/>
);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/ui/field.tsx` around lines 118 - 129, FieldTitle currently uses
the same data-slot as FieldLabel which conflicts with fieldVariants selectors;
change FieldTitle's data-slot from "field-label" to a unique value such as
"field-title" in the FieldTitle component so it no longer collides with
FieldLabel, and then update any related CSS/variant selectors that should target
the title slot (e.g., selectors referencing data-[slot=field-label] that are
meant for the title) to use data-[slot=field-title] or leave them untouched if
they should only target labels.

Comment on lines +19 to +22
className={cn(
"shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
className,
)}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm Radix Separator writes data-orientation (not data-horizontal/data-vertical)
fd -t f 'separator' node_modules/@radix-ui 2>/dev/null | head
rg -nP 'data-orientation|data-horizontal|data-vertical' -C2 --type=ts --type=tsx node_modules/@radix-ui 2>/dev/null | head -50

Repository: thyuhtooaung-dev/NextBlog

Length of output: 51


🏁 Script executed:

# Check repository structure and the separator component file
ls -la node_modules/@radix-ui 2>&1 | head -20

Repository: thyuhtooaung-dev/NextBlog

Length of output: 138


🏁 Script executed:

# Read the component file being reviewed
cat -n components/ui/separator.tsx 2>/dev/null || echo "File not found"

Repository: thyuhtooaung-dev/NextBlog

Length of output: 915


🏁 Script executed:

# Check if `@radix-ui/react-separator` is in package.json
rg '@radix-ui/react-separator' package.json 2>/dev/null || echo "Not found in package.json"

Repository: thyuhtooaung-dev/NextBlog

Length of output: 94


🏁 Script executed:

# Search the entire codebase for data-orientation or separator-related attributes
rg 'data-orientation|data-horizontal|data-vertical' --type=ts --type=tsx 2>/dev/null

Repository: thyuhtooaung-dev/NextBlog

Length of output: 51


🏁 Script executed:

# Check package.json for radix-related dependencies
cat package.json | grep -A5 -B5 "radix" 2>/dev/null | head -30

Repository: thyuhtooaung-dev/NextBlog

Length of output: 379


🏁 Script executed:

# Check tsconfig or next.config for path aliases
find . -maxdepth 2 -name "tsconfig.json" -o -name "next.config.*" | xargs cat 2>/dev/null | grep -A10 "paths\|alias" | head -50

Repository: thyuhtooaung-dev/NextBlog

Length of output: 255


🏁 Script executed:

# Search for where "radix-ui" might be defined as an alias or custom import
rg "from ['\"]radix-ui" --type=ts --type=tsx 2>/dev/null

Repository: thyuhtooaung-dev/NextBlog

Length of output: 51


🏁 Script executed:

# Check if there's a local radix-ui file/folder
find . -type d -name "radix-ui" 2>/dev/null
find . -type f -name "*radix*" -path "*/lib/*" -o -path "*/utils/*" 2>/dev/null

Repository: thyuhtooaung-dev/NextBlog

Length of output: 50381


🏁 Script executed:

# Find radix-ui in pnpm structure
find node_modules/.pnpm -type d -name "radix-ui*" 2>/dev/null | head -5

Repository: thyuhtooaung-dev/NextBlog

Length of output: 51


🏁 Script executed:

# Check for radix-ui Separator in pnpm structure and look for data-orientation
find node_modules/.pnpm -path "*radix-ui*/separator*" -type f 2>/dev/null | head -10

Repository: thyuhtooaung-dev/NextBlog

Length of output: 51


🏁 Script executed:

# Look for type definitions in radix-ui package
find node_modules/.pnpm -path "*radix-ui*" -name "*.d.ts" 2>/dev/null | grep -i separator | head -5

Repository: thyuhtooaung-dev/NextBlog

Length of output: 51


🏁 Script executed:

# Direct check for radix-ui in node_modules root
ls -la node_modules/ | grep radix

Repository: thyuhtooaung-dev/NextBlog

Length of output: 51


🏁 Script executed:

# Check if package is listed in package-lock or pnpm-lock
find . -maxdepth 2 -name "*lock*" -type f | head -3

Repository: thyuhtooaung-dev/NextBlog

Length of output: 85


🌐 Web query:

Radix UI Separator data-orientation attribute rendering

💡 Result:

The Radix UI Primitives Separator component renders a data-orientation attribute on its Root element based on the orientation prop. The orientation prop is an enum that defaults to "horizontal" and can be set to "vertical". The data attribute [data-orientation] receives the value "horizontal" or "vertical" accordingly. This attribute is intended for styling purposes, allowing CSS selectors like data-[orientation=vertical]:w-px. This behavior is documented in the official Radix Primitives API reference and confirmed across multiple documentation mirrors and implementations (e.g., Radix Vue uses the same pattern).

Citations:


🌐 Web query:

Tailwind CSS data-orientation variant syntax data[orientation=horizontal]

💡 Result:

The Tailwind CSS data-orientation variant syntax data[orientation=horizontal] refers to the data attribute variant data-[orientation=horizontal]:, introduced in Tailwind CSS v3.2. This is a dynamic variant that applies utility classes only when the element has a matching data-orientation="horizontal" attribute. Usage example: <div data-orientation="horizontal" class="data-[orientation=horizontal]:flex-row data-[orientation=vertical]:flex-col flex flex-col"> Content that changes flex direction based on data-orientation. </div> This generates CSS like .data-[orientation=horizontal]:flex-row[data-orientation=horizontal] { flex-direction: row; }. It supports group and peer variants, e.g., group-data-[orientation=horizontal]:bg-blue-500. This syntax is standard for components like scroll areas, sliders, and separators in libraries such as shadcn/ui, where data-orientation="horizontal" or "vertical" controls orientation-based styling. In Tailwind v4 (current as of 2026), dynamic data variants are enhanced for boolean attributes like data-current: but retain the data-[attr=value]: syntax for specific values. Note: Custom shorthands like data-horizontal: exist in some setups (e.g., shadcn/tailwind.css) but require explicit imports; the bracketed form is native and portable.

Citations:


🏁 Script executed:

# Find and examine tailwind config
find . -maxdepth 2 -name "tailwind.config.*" -type f | head -1 | xargs cat 2>/dev/null

Repository: thyuhtooaung-dev/NextBlog

Length of output: 51


🏁 Script executed:

# Search for tailwind config more broadly
find . -name "tailwind.config.*" -type f 2>/dev/null

Repository: thyuhtooaung-dev/NextBlog

Length of output: 51


🏁 Script executed:

# Check if tailwind is configured in package.json or other config files
cat package.json | grep -A 5 "tailwind"

Repository: thyuhtooaung-dev/NextBlog

Length of output: 411


🏁 Script executed:

# Look for any CSS files that might define custom variants
find . -maxdepth 3 -name "*.css" -type f 2>/dev/null | head -5

Repository: thyuhtooaung-dev/NextBlog

Length of output: 86


🏁 Script executed:

# Check the globals.css for Tailwind config or custom variants
cat app/globals.css

Repository: thyuhtooaung-dev/NextBlog

Length of output: 4373


🏁 Script executed:

# Search for any tailwind config in the codebase
find . -type f \( -name "*.ts" -o -name "*.js" -o -name "*.config.*" \) -exec grep -l "tailwind\|@layer" {} \; 2>/dev/null | head -10

Repository: thyuhtooaung-dev/NextBlog

Length of output: 1280


🏁 Script executed:

# Check if there's a postcss config
find . -maxdepth 2 -name "postcss.config.*" -type f 2>/dev/null | xargs cat 2>/dev/null

Repository: thyuhtooaung-dev/NextBlog

Length of output: 162


Fix Tailwind data attribute variants — separator will collapse without proper sizing.

Radix SeparatorPrimitive.Root renders data-orientation="horizontal" or "vertical", not boolean data-horizontal or data-vertical attributes. The current variants data-horizontal: and data-vertical: target non-existent attributes, so sizing rules (h-px w-full and w-px self-stretch) never apply.

🛠️ Proposed fix
       className={cn(
-        "shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
+        "shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-px data-[orientation=vertical]:self-stretch",
         className,
       )}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
className={cn(
"shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
className,
)}
className={cn(
"shrink-0 bg-border data[orientation=horizontal]:h-px data[orientation=horizontal]:w-full data[orientation=vertical]:w-px data[orientation=vertical]:self-stretch",
className,
)}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/ui/separator.tsx` around lines 19 - 22, The Tailwind
data-attribute variants are wrong: update the className in the Separator
component (the one building classes passed to SeparatorPrimitive.Root / cn) to
target Radix's data-orientation attribute instead of non-existent
data-horizontal/data-vertical; replace data-horizontal:... and data-vertical:...
with the attribute selector variants (e.g. data-[orientation=horizontal]:h-px
data-[orientation=horizontal]:w-full and data-[orientation=vertical]:w-px
data-[orientation=vertical]:self-stretch) so the horizontal/vertical sizing
rules actually apply.

@thyuhtooaung-dev thyuhtooaung-dev merged commit dd609b0 into main Apr 17, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant